/* SHELL */
#include "shell.h"
#include "video.h"
#include "keyboard.h"
#include "print.h"
#include "rtc.h"
#include "chat.h"

/* MACROS */
#define ISNUMERIC(x)	( ((x) >= '0')?(((x) <= '9')?1:0):0 )

/* variables globales */
/*TECLADO*/
extern unsigned char keyboardBuffer[];
extern int bufferFlag;
extern int bufferPosition;
/*CHAT*/
extern unsigned char chatBuffer[MAX_BUFF_CHAT+1];
extern int chatBufferPosition;



/* Tipo Comando - Especifica el comando a ejecutar en el shell */
typedef enum {CPUID=0, TIME, CHAT, CLEAR, FULL, HELP, NOT_YET} typeCommand;

/* Funciones de uso interno del Shell */
typeCommand getCommand(void);
int bufferCmp(char * string);
void changeDate(void);
void changeHour(void);
byte decToBCD(byte dec);
void chat_read(void);

/* Strings usados */
char mensaje[]=" *** Command not found ***\n";
char strchat[]="chat";
char strtime[]="time";
char strcpuid[]="cpuid";
char strclear[]="clear";
char strhelp[]="help";
char string_opciones1[]="\nCOMANDOS VALIDOS (shortcut)\n";
char string_opciones2[]="help \t (ctrl + h)\n";
char string_opciones3[]="chat\n";
char string_opciones4[]="clear \t (ctrl + c)\n";
char string_opciones5[]="cpuid \t (ctrl + p)\n";
char string_opciones6[]="time\n";
/* strings TIME */
char time_fecha[]="\n  1 - Cambio de Fecha\n";
char time_horario[]="  2 - Cambio de Horario\n";
char time_back[]="  Cualquier tecla para salir\n";
char fecha_dia[]="\n\tIngrese Dia:\t";
char fecha_mes[]="\n\tIngrese Mes:\t";
char fecha_anio[]="\n\tIngrese Anio:\t";
char horario_hora[]="\n\tIngrese Hora:\t";
char horario_min[]="\n\tIngrese Minutos:\t";
char horario_seg[]=" \n\tIngrese Segundos:\t";
char time_error[]="*** DATOS INCORRECTOS ***\n";
char time_option[]="\tOpcion: ";




/*
 * Se encarga de leer del buffer del teclado, parsear comandos y 
 * llamar a la funcion correspondiente si es un comando valido.
 * 
 * 	+	si esta lleno el buffer del chat, lo imprime en pantalla
 * 	+	si hay un caracter nuevo en el buffer del teclado lo parsea
 * 	+	si es un comando, llama a la funcion correspondiente.
 * 	+	si se lleno el buffer del teclado, lo vacia.
 */
void
shell(void)
{
	typeCommand comando;
	int i;
	showPrompt();
	while (1)
	{
		chat_read();/*consulta el buffer del chat y si esta lleno lo muestra en pantalla*/
		
		/* chequo de buffer de teclado */
		while (bufferFlag != 0)	/* me fijo si hay que imprimir en pantalla */
		{
			comando = getCommand();
	
			for (i=bufferFlag; i>0; i--)//POR SI LLEGARON LETRAS MUY RAPIDO
				print(keyboardBuffer[bufferPosition-i]); 
			bufferFlag=0;
					
			if (comando == CHAT)
			{
				chat_write();
			}
			if(comando == TIME)
				time();
			if(comando == CPUID)
				cpuid();
			if(comando == CLEAR)
				clear();
			if(comando == HELP)
				help();
			if(comando == FULL)
			{
				if(keyboardBuffer[0] != '\n')
					putString(mensaje);	
				showPrompt();
			}

		}	
	}	
	return;
}


/*
 * compara el comando que esta en el buffer del teclado
 * con los strings de los comandos validos.
 * si machea alguno, retorna el tipo de comando correspondiente, sino
 * retorna que no es un comando aun.
 */
typeCommand 
getCommand(void)
{
	typeCommand ret=NOT_YET;
	
	if( bufferCmp(strclear) )
		ret= CLEAR;
	else if( bufferCmp(strhelp) )
		ret = HELP;
	else if( bufferCmp(strtime) )
		ret = TIME;
	else if( bufferCmp(strcpuid) )
		ret = CPUID;
	else if( bufferCmp(strchat) )
		ret = CHAT;
	else if(keyboardBuffer[bufferPosition-1] == '\n')
	{
		ret= FULL;
	}
	return ret;
}

 
/*
 * borra la pantalla (excepto por la linea del RTC)
 * pone el cursor arriba de todo, y muestra el prompt.
 */ 
void
clear(void)
{
		k_clear_screen();
		setCursorPosition(0,0);
		showPrompt();
		bufferPosition=0;	
		return;
}
 
/*
 * muestra en pantalla los comandos posibles
 */
void
help(void)
{
	putString(string_opciones1);
	putString(string_opciones2);
	putString(string_opciones3);
	putString(string_opciones4);
	putString(string_opciones5);
	putString(string_opciones6);

	showPrompt();
	return;
}

/*
 * obtiene el vendor, el tipo de procesador y la marca
 * y los muestra en pantalla.
 */
void
cpuid(void)
{
	char * vendor;
	print('\n');
	print('\t');

	vendor=(char*)getVendorID();
	putString(vendor);

	print('\t');
	getProcessorInfo();
	print('\n');
	showPrompt();
	return;
}

/*
 * manda las teclas ingresadas en el buffer del teclado
 * al puerto serie. hasta que lea un '\n' o se llene el buffer
 */
void
chat_write(void)
{

	int stop=0,i;
	unsigned char letra;
	
	bufferPosition=0;
	bufferFlag=0;
	while( !stop )
	{
		if(bufferFlag !=0)
		{
			if( (letra=keyboardBuffer[bufferPosition-1]) == '\n' )
			{
				stop=1;				
			}
				for (i=bufferFlag; i>0; i--)//POR SI LLEGARON LETRAS MUY RAPIDO
				{
					print(letra);
					serialWrite(letra);
				}
			bufferFlag=0;
		}	
	}
	showPrompt();
	return;
}

/*
 * si el buffer del chat esta lleno, lo imprime todo en pantalla.
 * 
 */
void
chat_read(void)
{
	if(chatBuffer[chatBufferPosition-1] == '\n')
	{
		print('\n');
		putString(chatBuffer);
		chatBufferPosition=0;
		showPrompt();
	}
	return;
		
}


/*
 * permite al usuario cambiar la fecha o la hora del RTC
 *  al pedirle distintos comandos
 */
void
time(void)
{
	int stop=0, i;
	bufferPosition=0;
	putString(time_fecha);
	putString(time_horario);
	putString(time_back);
	putString(time_option);

	bufferPosition=0;
	while (!stop)	/* espero la opcion */
	{
		if( bufferFlag !=0)
		{
			if( keyboardBuffer[bufferPosition-1] == '\n')
				stop=1;
			
			for (i=bufferFlag; i>0; i--)//POR SI LLEGARON LETRAS MUY RAPIDO
				print(keyboardBuffer[bufferPosition-i]); 
			
			bufferFlag = 0;
		}
	}
	
	switch(leerNumBuffer())
	{
		case 1: /* llamo a cambia Fecha */
			 changeDate();
			 break;
		case 2: /* llamo a cambia Horario */
			 changeHour();
			 break;
		default: break;
	}	
	print('\n');
	showPrompt();
	return;
}

/*
 * es el sub-menu de time que pide parametros y
 * cambia la hora del RTC si los parametros recibidos son validos
 * 
 */
void
changeHour(void)
{
	byte hora, min, seg;
	int stop=0,i;

	putString(horario_hora);
	bufferPosition=0;
	while (!stop)	/* espero la opcion */
	{
		if( bufferFlag !=0)
		{
			if( keyboardBuffer[bufferPosition-1] == '\n')
				stop=1;
			
			for (i=bufferFlag; i>0; i--)//POR SI LLEGARON LETRAS MUY RAPIDO
				print(keyboardBuffer[bufferPosition-1]);
			bufferFlag = 0;
		}
	}
	hora=leerNumBuffer();
	
	putString(horario_min);
	bufferPosition=0;
	stop=0;
	while (!stop)	/* espero la opcion */
	{
		if( bufferFlag !=0)
		{
			if( keyboardBuffer[bufferPosition-1] == '\n')
				stop=1;
			for (i=bufferFlag; i>0; i--)//POR SI LLEGARON LETRAS MUY RAPIDO
				print(keyboardBuffer[bufferPosition-1]);
			bufferFlag = 0;
		}
	}
	min=leerNumBuffer();
			 
	putString(horario_seg);
	bufferPosition=0;
	stop=0;
	while (!stop)	/* espero la opcion */
	{
		if( bufferFlag !=0)
		{
			if( keyboardBuffer[bufferPosition-1] == '\n')
				stop=1;
			for (i=bufferFlag; i>0; i--)//POR SI LLEGARON LETRAS MUY RAPIDO
				print(keyboardBuffer[bufferPosition-1]);
			bufferFlag = 0;
		}
	}
	seg=leerNumBuffer();
	
	if(esHorarioOK(hora, min, seg))
	{
		hora=decToBCD(hora);
		min=decToBCD(min);
		seg=decToBCD(seg);

		putRTChours(hora);
		putRTCminutes(min);
		putRTCseconds(seg);
	}
	else 
	{
		putString(time_error);
	}
	return;
}


/*
 * es el sub-menu de time que pide parametros y
 * cambia la fecha del RTC si los parametros recibidos son validos
 * 
 */
void
changeDate(void)
{
	byte dia, mes, anio;
	int stop=0,i;
	
	putString(fecha_dia);
	bufferPosition=0;
	while (!stop)	/* espero la opcion */
	{
		if( bufferFlag !=0)
		{
			if( keyboardBuffer[bufferPosition-1] == '\n')
				stop=1;
			for (i=bufferFlag; i>0; i--)//POR SI LLEGARON LETRAS MUY RAPIDO
				print(keyboardBuffer[bufferPosition-1]);
			bufferFlag = 0;
		}
	}
	dia=leerNumBuffer();
			 
	putString(fecha_mes);
	bufferPosition=0;
	stop=0;
	while (!stop)	/* espero la opcion */
	{
		if( bufferFlag !=0)
		{
			if( keyboardBuffer[bufferPosition-1] == '\n')
				stop=1;
			for (i=bufferFlag; i>0; i--)//POR SI LLEGARON LETRAS MUY RAPIDO
				print(keyboardBuffer[bufferPosition-1]);
			bufferFlag = 0;
		}
	}
	mes=leerNumBuffer();
			 
	putString(fecha_anio);
	bufferPosition=0;
	stop=0;
	while (!stop)	/* espero la opcion */
	{
		if( bufferFlag !=0)
		{
			if( keyboardBuffer[bufferPosition-1] == '\n')
				stop=1;
			for (i=bufferFlag; i>0; i--)//POR SI LLEGARON LETRAS MUY RAPIDO
				print(keyboardBuffer[bufferPosition-1]);
			bufferFlag = 0;
		}
	}
	anio=leerNumBuffer();
			 
	if(esFechaOK(dia, mes, anio))
	{
		dia=decToBCD(dia);
		mes=decToBCD(mes);
		anio=decToBCD(anio);

		putRTCdayofmonth(dia);
		putRTCmonth(mes);
		putRTCyear(anio);
	}
	else
	{
		putString(time_error);
	}
	return;
}

/*
 * cambia de formato decimal a formato BCD
 */
byte
decToBCD(byte dec)
{
	byte bcd;

	bcd=dec/10;
	bcd= bcd<<4;
	
	bcd = bcd + dec%10;
	return bcd;
}

/*
 * compara el string con el estado actual del buffer.
 * retorna 1 si son iguales, 
 * 0 en caso contrario
 */
int
bufferCmp(char * string)
{
	int ret=1, i, stop=0;
	for(i=0; i<bufferPosition && ret && !stop; i++)
	{
		if( string[i]== '\0')
			stop=1;
		else if( string[i] != keyboardBuffer[i] )
			ret=0;
	}
	if( ret && stop  && bufferPosition == i && keyboardBuffer[i-1]=='\n' )
			ret=1;
	else
			ret=0;
	return ret;
}

/*
 * lee un numero de 2 digitos del buffer del teclado.
 * retorna el numero leido, o -1 si no era valido.
 */
int
leerNumBuffer(void)
{
       int acum=-1, aux;

       aux=keyboardBuffer[0];
       if( ISNUMERIC(aux) )
       {
               acum=aux-'0';

               aux=keyboardBuffer[1];
				if( aux !='\n')
				{
               		if( ISNUMERIC(aux) )
               		{
                       		acum*=10;
                       		acum+=aux-'0';

                       		aux=keyboardBuffer[2];
							if( aux != '\n')
								acum=-1;
						}
                       else 
                               acum=-1;
               }
               
       }
       return acum;
}
 
char daytab[2][13]={
       {0,31,28,31,30,31,30,31,31,30,31,30,31},
       {0,31,29,31,30,31,30,31,31,30,31,30,31}};
/*
 * chequea que la fecha sea valida
 * retorna 1 si es valida
 * 	0 en caso contrario
 */
int
esFechaOK(int dia, int mes, int anio)
{
		
       int leap=0;
       if( dia<1 || mes< 1 || mes> 12 || anio< 0 || anio>99)
               return 0;
       anio+=2000;
       /* considero que los 2 digitos del anio corresponden al 20xx no al 19xx*/
       leap = (anio%4 == 0) && (anio%100 != 0) || (anio%400 == 0);

       if( dia > daytab[leap][mes] )
               return 0;

       return 1;
}

/*
 * chequea que la hora sea valida
 *  retorna 1 si es valida
 * 	0 en caso contrario
 */
int
esHorarioOK(int hora, int min, int seg)
{
	if (hora < 0 || min < 0 || seg < 0 || hora > 23 || min > 59 || seg > 59)
		return 0;
	return 1;
}
